﻿using System;
using System.Collections.Generic;
using System.Reflection;
using NServiceBus;
using NServiceBus.Unicast.Transport;
using ObjectBuilder;

namespace NsbExtensions.Tests.Stubs
{
    public class TestBus : IBus
    {
        private readonly Dictionary<Type, IList<Type>> _messageHandlerMap = new Dictionary<Type, IList<Type>>();
        public List<SentMessage> PublishedMessages = new List<SentMessage>();
        public List<SentMessage> SentMessages = new List<SentMessage>();
        public ITransport Transport { get; set; }

        public SentMessage LastPublished { get; set; }
        public SentMessage LastSent { get; set; }

        public IBuilder Builder { get; set; }

        #region IBus Members

        public void Start()
        {
        }

        public void Publish<T>(params T[] messages) where T : IMessage
        {
            LastPublished = new SentMessage(null, messages[0]);
            PublishedMessages.Add(LastPublished);
        }

        public void Subscribe(Type messageType)
        {
        }

        public void Subscribe(Type messageType, Predicate<IMessage> condition)
        {
        }

        public void Unsubscribe(Type messageType)
        {
        }

        public void SendLocal(params IMessage[] messages)
        {
        }

        public ICallback Send(params IMessage[] messages)
        {
            return Send(null, messages);
        }

        public ICallback Send(string destination, params IMessage[] messages)
        {
            LastSent = new SentMessage(destination, messages[0]);
            SentMessages.Add(LastSent);
            return null;
        }

        public void Reply(params IMessage[] messages)
        {
            Send(messages);
        }

        public void Return(int errorCode)
        {
        }

        public void HandleCurrentMessageLater()
        {
        }

        public void DoNotContinueDispatchingCurrentMessageToHandlers()
        {
        }

        public string SourceOfMessageBeingHandled
        {
            get { return "Test"; }
        }

        public void Dispose()
        {
        }

        #endregion

        private void BuildAndDispatch(Type handlerType, IMessage message)
        {
            Builder.BuildAndDispatch(handlerType, "Handle", message);
        }

        public void ReceiveMessage(IMessage message)
        {
            foreach (Type t in GetHandlerTypes(typeof (IMessage)))
            {
                BuildAndDispatch(t, message);
            }

            foreach (Type t in GetHandlerTypes(message.GetType()))
            {
                BuildAndDispatch(t, message);
            }
        }

        public void LoadMessageHandlersFrom(Assembly asm)
        {
            foreach (Type t in asm.GetTypes())
            {
                If_Type_Is_MessageHandler_Then_Load(t);
            }
        }

        /// <summary>
        /// Evaluates a type and loads it if it implements IMessageHander<T>.
        /// </summary>
        /// <param name="t">The type to evaluate.</param>
        private void If_Type_Is_MessageHandler_Then_Load(Type t)
        {
            foreach (Type messageType in t.GetAllMessageTypesHandled())
            {
                RegisterHandlerTypeForMessageType(t, messageType);
            }
        }


        /// <summary>
        /// Registers a relationship between a message type and a handler for that type.
        /// </summary>
        /// <param name="handlerType">The type of the handler.</param>
        /// <param name="messageType">The type of the message to associate with the handler.</param>
        private void RegisterHandlerTypeForMessageType(Type handlerType, Type messageType)
        {
            if (!_messageHandlerMap.ContainsKey(messageType))
                _messageHandlerMap.Add(messageType, new List<Type>());

            if (!_messageHandlerMap[messageType].Contains(handlerType))
            {
                _messageHandlerMap[messageType].Add(handlerType);
            }
        }

        /// <summary>
        /// Gets a list of handler types associated with a message type.
        /// </summary>
        /// <param name="messageType">The type of message to get the handlers for.</param>
        /// <returns>The list of handler types associated with the message type.</returns>
        private IList<Type> GetHandlerTypes(Type messageType)
        {
            IList<Type> result;
            _messageHandlerMap.TryGetValue(messageType, out result);

            if (result == null)
                result = new List<Type>();

            return result;
        }
    }

    public class SentMessage
    {
        public SentMessage(string to, IMessage message)
        {
            To = to;
            Message = message;
        }

        public string To { get; set; }
        public IMessage Message { get; set; }
    }

    public class TestCallback : ICallback
    {
        #region ICallback Members

        public IAsyncResult Register(AsyncCallback callback, object state)
        {
            return null;
        }

        #endregion
    }
}